Modifying original code

Original file: Fig3_4_6_ExtFig5-6-10_RPM_RPMA_Allos_CellTag.R

Only CellTag analysis included in this notebook.

Visualizing Celltag/clone data

From table above, added whether clones were robust or not
Robust defined as >5 cells per clone post-QC.
CellTag metadata: clone information can also be found in Supplementary Table 4 of Ireland et al, 2025

Idents(TBO_seurat) <- 'Robust'
table(TBO_seurat@meta.data$Robust, TBO_seurat@meta.data$UnID)
        
         RPMA_Allo RPM_Allo3 RPM_Allo4 RPM_Allo_New
  No          6659      5244      6523         2227
  Robust      3501      1474       337          653
table(TBO_seurat@meta.data$Robust, TBO_seurat@meta.data$GenoCT)
        
         RPMA_CTpreCre RPM_CTpostCre RPM_CTpreCre
  No              6659         11767         2227
  Robust          3501          1811          653

Identify robust clones

table(clones@meta.data$Robust, clones@meta.data$Genotype)
        
          RPM RPMA
  Robust 2464 3501

Assess clones by leiden cluster

Fig. 3d

First, just look at bar graph, no particular order #

Idents(clones) <- 'leiden_scVI_1.2'

x <- table(clones@meta.data$CellTag_Clone,Idents(clones))
proportions <- as.data.frame(100*prop.table(x, margin = 1))

# proportions$Cluster
colnames(proportions)<-c("Cluster", "Sample", "Frequency")

# Stacked
p <- ggplot(proportions, aes(fill=Sample, y=Frequency, x=Cluster)) + 
  geom_bar(position="stack", stat="identity")

p + scale_fill_manual(values=my_colors) + 
  theme_bw()+ theme(axis.text.y = element_text(size=20), 
                    axis.text.x=element_text(size=14), axis.title.x =element_text(size=14), 
                    axis.title.y = element_text(size=18), legend.text = element_text(size=12), 
                    legend.title = element_text(size=18))+rotate_x_text(size=7,angle = 90)


dat <- p$data

Transform data to perform hierarchical (agglomerative) clustering

# Pivot to wide format: Cluster × Sample
mat <- dat %>%
  pivot_wider(names_from = Sample, values_from = Frequency, values_fill = 0) %>%
  tibble::column_to_rownames("Cluster") %>%
  as.matrix()

mat_norm <- prop.table(mat, margin = 1)  # normalize each row to sum to 1

Hierarchical (agglomerative) clustering of clones with default parameters in pheatmap()

hm <- pheatmap::pheatmap(t(mat), cutree_rows = 1, cutree_cols = 8, cellwidth = 5, 
                         cellheight = 5, fontsize = 8,
                         cluster_rows=FALSE, border_color=NA, 
                         color = colorRampPalette(c("darkturquoise","black","red2"))(30))


clone_order <- colnames(t(mat))[hm$tree_col$order]

Fig 3d

Clustered clones by proportions of cells in Leiden clusters

# Now, plot clones by Leiden, ordered, final # (Fig. 3d)
Idents(clones)<-'leiden_scVI_1.2'

x <- table(clones@meta.data$CellTag_Clone,Idents(clones))
proportions <- as.data.frame(100*prop.table(x, margin = 1))

colnames(proportions) <- c("Cluster", "Sample", "Frequency")
proportions$Cluster <- factor(proportions$Cluster, clone_order)

# Stacked
p <- ggplot(proportions, aes(fill=Sample, y=Frequency, x=Cluster)) + 
    geom_bar(position="stack", stat="identity")

p + scale_fill_manual(values=my_colors) + 
  theme_bw() + theme(axis.text.y = element_text(size=20), 
                     axis.text.x=element_text(size=14), axis.title.x =element_text(size=14), 
                     axis.title.y = element_text(size=18), legend.text = element_text(size=12), 
                     legend.title = element_text(size=18))+rotate_x_text(size=7,angle = 90) + 
    labs(x = NULL, y = "% of clone")

Idents(clones) <- 'Clone_Dynamics'
table(clones@meta.data$Clone_Dynamics)

Pattern_1 Pattern_2 Pattern_3 Pattern_4 Pattern_5 Unknown_1 Unknown_2 
      553       675       693      2797      1032       167        48 

Function for plotting clone dynamics

clone_patterns <- c('Pattern_1', 'Pattern_2', 'Pattern_3', 'Pattern_4', 'Pattern_5', 'Unknown_1', 'Unknown_2')
pattern_colors <- c('orange', 'green2', 'red', 'royalblue2', 'purple', 'gray40', 'black')
names(pattern_colors) <- clone_patterns
plotCloneDyn <- function(clone_pattern, pattern_color, tit = "") {
    # Get cells of interest
    ss <- subset(clones, idents = clone_pattern)
    highlighted_cells <- rownames(ss@meta.data)
    
    # Plot all cells in grey, highlight the pattern in your chosen color
    DimPlot(clones, 
            reduction = "fa", 
            cells.highlight = highlighted_cells,
            sizes.highlight = 0.25,
            cols.highlight = pattern_color,
            cols = "grey90", 
            pt.size = 0.1) + 
    ggtitle(tit) & NoLegend() & NoAxes()
}

Fig 3e

ForceAtlas embedding showing representative clones of each pattern with cells colored by pattern

cdp <- (lapply(1:7, function(i) plotCloneDyn(clone_patterns[i], pattern_colors[i], tit=clone_patterns[i])))
p_combined <- ggarrange(plotlist = cdp, ncol = 4, nrow = 2)
p_combined

getCloneCellIDs <- function(id) {
    cellIDs <- rownames(subset(clones,idents=c(id))@meta.data)
}
clone_cell_ids <- lapply(setNames(clone_patterns, clone_patterns), getCloneCellIDs)
plotPhenoWithBackground <- function(clone_pattern, pheno_col, title = "", seurat_obj = clones) {
    # Check if 'fa' reduction exists
    if (!"fa" %in% names(seurat_obj@reductions)) {
        stop("The 'fa' reduction is not found in the Seurat object.")
    }

    # Extract FA coordinates and metadata
    coords <- as.data.frame(Embeddings(seurat_obj, reduction = "fa"))
    coords$cell <- rownames(coords)

    # Get metadata
    meta <- seurat_obj@meta.data[, c("Clone_Dynamics", "Pheno")]
    meta$cell <- rownames(meta)

    # Merge coordinates and metadata
    dat <- merge(coords, meta, by = "cell")

    # Flag highlighted cells
    dat$highlight <- dat$Clone_Dynamics == clone_pattern
    dat$Pheno <- droplevels(dat$Pheno)

    # Split background and foreground
    bg_dat <- dat[!dat$highlight, ]
    fg_dat <- dat[dat$highlight, ]

    if (nrow(fg_dat) == 0) {
        warning(paste("No cells found for Clone_Dynamics pattern:", clone_pattern))
        return(ggplot() + ggtitle(paste(clone_pattern, "(no cells)")))
    }

    # Plot
    p <- ggplot() +
        geom_point(data = bg_dat, aes(x = FA_1, y = FA_2), color = "gray90", size = 0.2) +
        geom_point(data = fg_dat, aes(x = FA_1, y = FA_2, color = Pheno), size = 1) +
        scale_color_manual(values = pheno_col, drop = FALSE) +
        ggtitle(title) +
        theme_void() +
        theme(
            legend.position = "none",
            panel.background = element_rect(fill = "transparent", color = NA),
            plot.background = element_rect(fill = "transparent", color = NA)
        )

    return(p)
}

ForceAtlas embedding showing cells colored by SCLC phenotype

DimPlot(clones, group.by='Pheno', cols=pheno_col, reduction='fa', label=FALSE, label.size=6, pt.size = 0.05) & 
    NoAxes()

Fig 3f

ForceAtlas embedding showing representative clones of each pattern with cells colored by SCLC phenotype

plots <- lapply(clone_patterns, function(pat) plotPhenoWithBackground(pat, pheno_col, title = pat))
p_combined <- ggarrange(plotlist = plots, ncol = 4, nrow = 2)
p_combined

Visualize individual clones

Ext Data Fig. 6b,c

plotPatternDynByClone <- function(clone_pattern) {
    Idents(clones)<-'Clone_Dynamics'
    p1 <- subset(clones,idents=c(clone_pattern))
    
    pattern_color <- pattern_colors[clone_pattern]

    test <- as.data.frame(p1$CellTag_Clone)
    test$Barcodes <- rownames(test)
    # Group by CellTag_Clone
    test <- test %>% group_by(p1$CellTag_Clone)
    test <- dplyr::group_split(test)
    
    n_clones <- length(test)
    # Plot in for loop all RPM clones in Pattern 1
    plot_lst <- vector("list", length = n_clones)
    for (i in seq(n_clones)) {
      g <- DimPlot(TBO_seurat, group.by="CellTag_Clone", reduction='fa', order=TRUE, 
                   cells.highlight=test[[i]]$Barcodes,sizes.highlight=2, 
                   cols.highlight=pattern_color) +
          ggtitle(paste0(test[[i]]$`p1$CellTag_Clone`[1])) + 
          theme(plot.title = element_text(size = 8,face = "plain")) & NoLegend() & 
          NoAxes()
      plot_lst[[i]] <- g
    }
    
    # Combine multiple plots for output, as desired
    return(cowplot::plot_grid(plotlist = plot_lst, ncol=5))
}
pattern_plot_list <- lapply(clone_patterns, plotPatternDynByClone)

n_plots_per_pattern <- sapply(pattern_plot_list, function(x) length(x[['layers']]))

## calculate height of figure based on number of plots per pattern
plt_height <- 2.667 * ((n_plots_per_pattern %/% 5) + 1)

Ext Data Fig 6b,c

pattern_plot_list[[1]]

pattern_plot_list[[2]]

pattern_plot_list[[3]]

pattern_plot_list[[4]]

pattern_plot_list[[5]]

pattern_plot_list[[6]]

pattern_plot_list[[7]]

Visualize only clones matching in vivo in FA projection

Ext Data Fig. 7f

Idents(clones) <- "CellTag_Clone"
# Subset just the clones that match the in vitro, pattern 1
invitromatch <- subset(clones, idents=c("RPM_Clone_14","RPM_Clone_2","RPM_Clone_33","RPM_Clone_36","RPM_Clone_6"))

p1_cells <- colnames(invitromatch)
p2_cells <- colnames(subset(clones, idents="RPM_Clone_23"))
p5_cells <- colnames(subset(clones, idents="RPM_Clone_13"))

DimPlot(TBO_seurat, group.by="CellTag_Clone", reduction='fa', 
        order=TRUE, cells.highlight=p1_cells, sizes.highlight=2, 
        cols.highlight=c("orange")) + ggtitle("") & NoLegend() & NoAxes()

DimPlot(TBO_seurat, group.by="CellTag_Clone", reduction='fa', 
        order=TRUE, cells.highlight=p2_cells, sizes.highlight=2, 
        cols.highlight=pattern_colors["Pattern_2"]) + ggtitle("") & NoLegend() & NoAxes()

DimPlot(TBO_seurat, group.by="CellTag_Clone", reduction='fa', 
        order=TRUE, cells.highlight=p5_cells, sizes.highlight=2, 
        cols.highlight=pattern_colors["Pattern_5"]) + ggtitle("") & NoLegend() & NoAxes()

NA
NA

Fig. 3g

dpt psuedotime in FA space

FeaturePlot(TBO_seurat, features = c("dpt_pseudotime"), pt.size=0.01,
            reduction='fa',) + scale_color_viridis(option="viridis",direction=-1)& NoAxes()

Function to plot clone dynamics colored by DPT

plotCloneDynDpt <- function(clone_pattern, seurat_obj, title = "") {
    # Subset cells of interest
    ss <- subset(seurat_obj, idents = clone_pattern)
    highlight_cells <- colnames(ss)

    # Extract embeddings and metadata
    fa_coords <- Embeddings(seurat_obj, "fa")
    dpt_vals <- seurat_obj@meta.data$dpt_pseudotime
    names(dpt_vals) <- rownames(seurat_obj@meta.data)

    dat <- as.data.frame(fa_coords)
    dat$highlight <- ifelse(rownames(dat) %in% highlight_cells, "yes", "no")
    dat$pseudotime <- dpt_vals[rownames(dat)]
    colnames(dat)[1:2] <- c("FA1", "FA2")  # name columns for clarity

    library(ggplot2)
    ggplot(dat, aes(x = FA1, y = FA2)) +
        # Background cells
        geom_point(data = subset(dat, highlight == "no"), 
                   color = "grey85", size = 0.1) +
        # Highlighted cells colored by pseudotime
        geom_point(data = subset(dat, highlight == "yes"), 
                   aes(color = pseudotime), size = 0.25) +
        scale_color_viridis_c(option = "turbo", direction = -1) +
        ggtitle(title) +
        theme_void() +
        theme(
            legend.position = "none",
            panel.border = element_blank(),      # removes any panel border
            plot.background = element_rect(fill = "transparent", color = NA),
            panel.background = element_rect(fill = "transparent", color = NA),
            panel.grid.major = element_blank(),
            panel.grid.minor = element_blank()
        )
}

Fig 3h

Idents(clones) <- 'Clone_Dynamics'

my_pats <- c('Pattern_1','Pattern_2','Pattern_5','Unknown_1','Pattern_3','Pattern_4')

plots <- lapply(my_pats, function(pat) {
    tit <- gsub(pat, '_', ' ')
    plotCloneDynDpt(pat, clones, title = tit)
})

cowplot::plot_grid(plotlist = plots, ncol=6)

LS0tCnRpdGxlOiAiUlBNX1JQTUFfQWxsb3NfQ2VsbFRhZyBub3RlYm9vayIKYXV0aG9yOiAiQWJiaWUgSXJlbGFuZCwgRGFycmVuIFR5c29uIgpkYXRlOiAiMjAyNS0wNi0yNCIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKIyMjIE1vZGlmeWluZyBvcmlnaW5hbCBjb2RlCk9yaWdpbmFsIGZpbGU6IFtGaWczXzRfNl9FeHRGaWc1LTYtMTBfUlBNX1JQTUFfQWxsb3NfQ2VsbFRhZy5SXShQcmVwcm9jZXNzaW5nX2Zvcl9yZWZlcmVuY2UvUl9Db2RlL0ZpZzNfNF82X0V4dEZpZzUtNi0xMF9SUE1fUlBNQV9BbGxvc19DZWxsVGFnLlIpICAKCk9ubHkgQ2VsbFRhZyBhbmFseXNpcyBpbmNsdWRlZCBpbiB0aGlzIG5vdGVib29rLgoKIyMjIFJlbGF0ZWQgdG86CiogRmlnIDNjLWgKKiBFeHRlbmRlZCBEYXRhIEZpZyA2YixjCiogRXh0ZW5kZWQgRGF0YSBGaWcgN2YKCmBgYHtyfQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMoewogICAgbGlicmFyeShTZXVyYXQpCiAgICBsaWJyYXJ5KFNldXJhdE9iamVjdCkKICAgIGxpYnJhcnkoU3VtbWFyaXplZEV4cGVyaW1lbnQpCiAgICBsaWJyYXJ5KGdncGxvdDIpCiAgICBsaWJyYXJ5KGdncHVicikKICAgIGxpYnJhcnkodGlkeXIpCiAgICBsaWJyYXJ5KHBhdGNod29yaykKICAgIGxpYnJhcnkodmlyaWRpcykKfSkKYGBgCmBgYHtyfQpTQVZFRklHUyA8LSBGQUxTRQpgYGAKCgpgYGB7cn0KVEJPX3NldXJhdDwtcmVhZFJEUygiLi4vZGF0YS8wNV8yMDI1X1JQTV9SUE1BX1RCT19DZWxsVGFnX1NldXJhdF93U2lnc19GQV9kcHRfZmluYWwucmRzIikKVEJPX3NldXJhdApgYGAKCmBgYHtyfQpjbG9uZXMgPC0gcmVhZFJEUygiLi4vZGF0YS8wNV8yMDI1X1JQTV9SUE1BX1RCT0FsbG9fQ2VsbFRhZ0Nsb25lc19Pbmx5Y2xvbmVzLnJkcyIpCmBgYAoKYGBge3J9Cm15X2NvbG9ycyA8LSBjKAogICIjRTQxQTFDIiwgIyBzdHJvbmcgcmVkCiAgIiMzNzdFQjgiLCAjIG1lZGl1bSBibHVlCiAgIiM0REFGNEEiLCAjIGdyZWVuCiAgIiM5ODRFQTMiLCAjIHB1cnBsZQogICIjRkY3RjAwIiwgIyBvcmFuZ2UKICAiI0ZGRkYzMyIsICMgeWVsbG93CiAgIiNBNjU2MjgiLCAjIGJyb3duCiAgIiNlNzI5OGEiLCAjIHBpbmsKICAiIzY2NjY2NiIsICMgZ3JleQogICIjNjZDMkE1IiwgIyB0ZWFsCiAgIiNGQzhENjIiLCAjIHNhbG1vbgogICIjOERBMENCIiwgIyBzb2Z0IGJsdWUKICAiI0U3OEFDMyIsICMgc29mdCBwaW5rIChkaWZmZXJlbnQgZnJvbSA4KQogICIjQTZEODU0IiwgIyBsaWdodCBncmVlbiAoYnV0IHllbGxvd2lzaCB0aW50LCBub3QgZ3JlZW4pCiAgIiNGRkQ5MkYiLCAjIGxlbW9uIHllbGxvdwogICIjRTVDNDk0IiwgIyBsaWdodCBicm93bgogICIjQjNCM0IzIiwgIyBsaWdodCBncmV5CiAgIiMxQjlFNzciLCAjIGRlZXAgdGVhbAogICIjRDk1RjAyIiwgIyBkYXJrIG9yYW5nZQogICIjNzU3MEIzIiwgIyBzdHJvbmcgcHVycGxlCiAgIiM2NkE2MUUiICAjIG9saXZlIGdyZWVuIChOT1Qgc2FtZSBncmVlbiBhcyBiZWZvcmUpCikKCnBoZW5vX2NvbCA8LSBjKCJicm93bjIiLCJkYXJrb3JjaGlkNCIsImRvZGdlcmJsdWUiLCIjNjZBNjFFIiwib3JhbmdlIiwidHVycXVvaXNlNCIsInR1cnF1b2lzZSIpCm5hbWVzKHBoZW5vX2NvbCkgPC0gYygiTkUiLCJORS9OZXVyb25hbCIsIk5ldXJvbmFsIiwiQVRPSDEiLCJUdWZ0IiwiVHJpcGxlLU5lZyIsIkJhc2FsIikKYGBgCgojIyMjIEZpZyAzYwpGb3JjZUF0bGFzIGVtYmVkZGluZyBvZiBSUE0gYW5kIFJQTUEgQ2VsbFRhZ2dlZCBhbGxvZ3JhZnRlZCB0dW1vciBjZWxscwpgYGB7ciBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD0zLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpEaW1QbG90KFRCT19zZXVyYXQsIGdyb3VwLmJ5PSdsZWlkZW5fc2NWSV8xLjInLCBjb2xzPW15X2NvbG9ycywgcmVkdWN0aW9uPSdmYScsIGxhYmVsPVRSVUUsIGxhYmVsLnNpemU9NCkgJiAKICAgIE5vQXhlcygpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikgIyBzdXBwcmVzcyBsZWdlbmQKCmBgYAoKYGBge3IgZmlnLmhlaWdodD0zLCBmaWcud2lkdGg9NC41LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpEaW1QbG90KFRCT19zZXVyYXQsIGdyb3VwLmJ5PSdQaGVubycsIGNvbHM9cGhlbm9fY29sLCByZWR1Y3Rpb249J2ZhJywgbGFiZWw9RkFMU0UsIGxhYmVsLnNpemU9NikgJiAKICAgIE5vQXhlcygpCmBgYAoKYGBge3IgZmlnLmhlaWdodD0zLCBmaWcud2lkdGg9NiwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyBFeHRyYWN0IGNvb3JkaW5hdGVzIGZyb20gZm9yY2UtZGlyZWN0ZWQgbGF5b3V0IChvciBhbnkgbGF5b3V0KQpkYXQgPC0gRW1iZWRkaW5ncyhUQk9fc2V1cmF0LCByZWR1Y3Rpb24gPSAiZmEiKSAlPiUgCiAgYXMuZGF0YS5mcmFtZSgpICU+JQogIG11dGF0ZShDbHVzdGVyID0gVEJPX3NldXJhdCRsZWlkZW5fc2NWSV8xLjIsCiAgICAgICAgIEdlbm90eXBlID0gVEJPX3NldXJhdCRHZW5vdHlwZSkKCiMgU2V0IHVwIHlvdXIgY29sb3IgdmVjdG9yIChuYW1lZCkKbXlfY29sb3JzX25hbWVkIDwtIHNldE5hbWVzKG15X2NvbG9ycywgbGV2ZWxzKFRCT19zZXVyYXQkbGVpZGVuX3NjVklfMS4yKSkKCiMgUGxvdCBlYWNoIEdlbm90eXBlIHdpdGggYmFja2dyb3VuZCBjZWxscyBncmV5ZWQKcGxvdHMgPC0gbGFwcGx5KHVuaXF1ZShkYXQkR2Vub3R5cGUpLCBmdW5jdGlvbihnKSB7CiAgZGF0JGhpZ2hsaWdodCA8LSBpZmVsc2UoZGF0JEdlbm90eXBlID09IGcsICJoaWdobGlnaHQiLCAiYmFja2dyb3VuZCIpCiAgZGF0JENvbG9yIDwtIGlmZWxzZShkYXQkR2Vub3R5cGUgPT0gZywgbXlfY29sb3JzX25hbWVkW2RhdCRDbHVzdGVyXSwgImxpZ2h0Z3JleSIpCgogIGdncGxvdChkYXQsIGFlcyh4ID0gRkFfMSwgeSA9IEZBXzIpKSArCiAgICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IENvbG9yKSwgc2l6ZSA9IDAuNSkgKwogICAgc2NhbGVfY29sb3JfaWRlbnRpdHkoKSArCiAgICBnZ3RpdGxlKGcpICsKICAgIHRoZW1lX3ZvaWQoKSArCiAgICB0aGVtZSgKICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSksCiAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgcGxvdC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAidHJhbnNwYXJlbnQiLCBjb2xvciA9IE5BKQogICAgKQp9KQoKIyBDb21iaW5lIGludG8gb25lIGZpZ3VyZQphIDwtIHdyYXBfcGxvdHMocGxvdHMsIG5jb2wgPSAyKQphCgppZihTQVZFRklHUykgZ2dzYXZlKCJGQWJ5R2Vuby5wbmciLCBwbG90PSBhLCB3aWR0aD02LCBoZWlnaHQ9MywgZHBpPTMwMCwgYmcgPSAidHJhbnNwYXJlbnQiKQpgYGAKCgoKIyMjIFZpc3VhbGl6aW5nIENlbGx0YWcvY2xvbmUgZGF0YQoKRnJvbSB0YWJsZSBhYm92ZSwgYWRkZWQgd2hldGhlciBjbG9uZXMgd2VyZSByb2J1c3Qgb3Igbm90ICAKUm9idXN0IGRlZmluZWQgYXMgPjUgY2VsbHMgcGVyIGNsb25lIHBvc3QtUUMuICAKQ2VsbFRhZyBtZXRhZGF0YTogY2xvbmUgaW5mb3JtYXRpb24gY2FuIGFsc28gYmUgZm91bmQgaW4gU3VwcGxlbWVudGFyeSBUYWJsZSA0IG9mIElyZWxhbmQgZXQgYWwsIDIwMjUgIApgYGB7cn0KSWRlbnRzKFRCT19zZXVyYXQpIDwtICdSb2J1c3QnCmBgYAoKCmBgYHtyfQp0YWJsZShUQk9fc2V1cmF0QG1ldGEuZGF0YSRSb2J1c3QsIFRCT19zZXVyYXRAbWV0YS5kYXRhJFVuSUQpCnRhYmxlKFRCT19zZXVyYXRAbWV0YS5kYXRhJFJvYnVzdCwgVEJPX3NldXJhdEBtZXRhLmRhdGEkR2Vub0NUKQpgYGAKIyMjIElkZW50aWZ5IHJvYnVzdCBjbG9uZXMKYGBge3J9CnRhYmxlKGNsb25lc0BtZXRhLmRhdGEkUm9idXN0LCBjbG9uZXNAbWV0YS5kYXRhJEdlbm90eXBlKQpgYGAKCiMjIyBBc3Nlc3MgY2xvbmVzIGJ5IGxlaWRlbiBjbHVzdGVyCiMjIyMgRmlnLiAzZCAgCkZpcnN0LCBqdXN0IGxvb2sgYXQgYmFyIGdyYXBoLCBubyBwYXJ0aWN1bGFyIG9yZGVyICMKCmBgYHtyIGZpZy5oZWlnaHQ9NCwgZmlnLndpZHRoPTEwfQpJZGVudHMoY2xvbmVzKSA8LSAnbGVpZGVuX3NjVklfMS4yJwoKeCA8LSB0YWJsZShjbG9uZXNAbWV0YS5kYXRhJENlbGxUYWdfQ2xvbmUsSWRlbnRzKGNsb25lcykpCnByb3BvcnRpb25zIDwtIGFzLmRhdGEuZnJhbWUoMTAwKnByb3AudGFibGUoeCwgbWFyZ2luID0gMSkpCgojIHByb3BvcnRpb25zJENsdXN0ZXIKY29sbmFtZXMocHJvcG9ydGlvbnMpPC1jKCJDbHVzdGVyIiwgIlNhbXBsZSIsICJGcmVxdWVuY3kiKQoKIyBTdGFja2VkCnAgPC0gZ2dwbG90KHByb3BvcnRpb25zLCBhZXMoZmlsbD1TYW1wbGUsIHk9RnJlcXVlbmN5LCB4PUNsdXN0ZXIpKSArIAogIGdlb21fYmFyKHBvc2l0aW9uPSJzdGFjayIsIHN0YXQ9ImlkZW50aXR5IikKCnAgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9bXlfY29sb3JzKSArIAogIHRoZW1lX2J3KCkrIHRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemU9MjApLCAKICAgICAgICAgICAgICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoc2l6ZT0xNCksIGF4aXMudGl0bGUueCA9ZWxlbWVudF90ZXh0KHNpemU9MTQpLCAKICAgICAgICAgICAgICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZT0xOCksIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MTIpLCAKICAgICAgICAgICAgICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xOCkpK3JvdGF0ZV94X3RleHQoc2l6ZT03LGFuZ2xlID0gOTApCgpkYXQgPC0gcCRkYXRhCmBgYApUcmFuc2Zvcm0gZGF0YSB0byBwZXJmb3JtIGhpZXJhcmNoaWNhbCAoYWdnbG9tZXJhdGl2ZSkgY2x1c3RlcmluZwpgYGB7cn0KIyBQaXZvdCB0byB3aWRlIGZvcm1hdDogQ2x1c3RlciDDlyBTYW1wbGUKbWF0IDwtIGRhdCAlPiUKICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gU2FtcGxlLCB2YWx1ZXNfZnJvbSA9IEZyZXF1ZW5jeSwgdmFsdWVzX2ZpbGwgPSAwKSAlPiUKICB0aWJibGU6OmNvbHVtbl90b19yb3duYW1lcygiQ2x1c3RlciIpICU+JQogIGFzLm1hdHJpeCgpCgptYXRfbm9ybSA8LSBwcm9wLnRhYmxlKG1hdCwgbWFyZ2luID0gMSkgICMgbm9ybWFsaXplIGVhY2ggcm93IHRvIHN1bSB0byAxCmBgYAoKSGllcmFyY2hpY2FsIChhZ2dsb21lcmF0aXZlKSBjbHVzdGVyaW5nIG9mIGNsb25lcyB3aXRoIGRlZmF1bHQgcGFyYW1ldGVycyBpbiBgcGhlYXRtYXAoKWAKYGBge3IgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9OH0KaG0gPC0gcGhlYXRtYXA6OnBoZWF0bWFwKHQobWF0KSwgY3V0cmVlX3Jvd3MgPSAxLCBjdXRyZWVfY29scyA9IDgsIGNlbGx3aWR0aCA9IDUsIAogICAgICAgICAgICAgICAgICAgICAgICAgY2VsbGhlaWdodCA9IDUsIGZvbnRzaXplID0gOCwKICAgICAgICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJfcm93cz1GQUxTRSwgYm9yZGVyX2NvbG9yPU5BLCAKICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gY29sb3JSYW1wUGFsZXR0ZShjKCJkYXJrdHVycXVvaXNlIiwiYmxhY2siLCJyZWQyIikpKDMwKSkKCmNsb25lX29yZGVyIDwtIGNvbG5hbWVzKHQobWF0KSlbaG0kdHJlZV9jb2wkb3JkZXJdCmBgYAoKCiMjIyMgRmlnIDNkCkNsdXN0ZXJlZCBjbG9uZXMgYnkgcHJvcG9ydGlvbnMgb2YgY2VsbHMgaW4gTGVpZGVuIGNsdXN0ZXJzCmBgYHtyIGZpZy5oZWlnaHQ9NywgZmlnLndpZHRoPTEwfQojIE5vdywgcGxvdCBjbG9uZXMgYnkgTGVpZGVuLCBvcmRlcmVkLCBmaW5hbCAjIChGaWcuIDNkKQpJZGVudHMoY2xvbmVzKTwtJ2xlaWRlbl9zY1ZJXzEuMicKCnggPC0gdGFibGUoY2xvbmVzQG1ldGEuZGF0YSRDZWxsVGFnX0Nsb25lLElkZW50cyhjbG9uZXMpKQpwcm9wb3J0aW9ucyA8LSBhcy5kYXRhLmZyYW1lKDEwMCpwcm9wLnRhYmxlKHgsIG1hcmdpbiA9IDEpKQoKY29sbmFtZXMocHJvcG9ydGlvbnMpIDwtIGMoIkNsdXN0ZXIiLCAiU2FtcGxlIiwgIkZyZXF1ZW5jeSIpCnByb3BvcnRpb25zJENsdXN0ZXIgPC0gZmFjdG9yKHByb3BvcnRpb25zJENsdXN0ZXIsIGNsb25lX29yZGVyKQoKIyBTdGFja2VkCnAgPC0gZ2dwbG90KHByb3BvcnRpb25zLCBhZXMoZmlsbD1TYW1wbGUsIHk9RnJlcXVlbmN5LCB4PUNsdXN0ZXIpKSArIAogICAgZ2VvbV9iYXIocG9zaXRpb249InN0YWNrIiwgc3RhdD0iaWRlbnRpdHkiKQoKcCArIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1teV9jb2xvcnMpICsgCiAgdGhlbWVfYncoKSArIHRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemU9MjApLCAKICAgICAgICAgICAgICAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KHNpemU9MTQpLCBheGlzLnRpdGxlLnggPWVsZW1lbnRfdGV4dChzaXplPTE0KSwgCiAgICAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplPTE4KSwgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0xMiksIAogICAgICAgICAgICAgICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xOCkpK3JvdGF0ZV94X3RleHQoc2l6ZT03LGFuZ2xlID0gOTApICsgCiAgICBsYWJzKHggPSBOVUxMLCB5ID0gIiUgb2YgY2xvbmUiKQoKYGBgCgpgYGB7cn0KSWRlbnRzKGNsb25lcykgPC0gJ0Nsb25lX0R5bmFtaWNzJwp0YWJsZShjbG9uZXNAbWV0YS5kYXRhJENsb25lX0R5bmFtaWNzKQpgYGAKCiMjIyBGdW5jdGlvbiBmb3IgcGxvdHRpbmcgY2xvbmUgZHluYW1pY3MKCmBgYHtyfQpjbG9uZV9wYXR0ZXJucyA8LSBjKCdQYXR0ZXJuXzEnLCAnUGF0dGVybl8yJywgJ1BhdHRlcm5fMycsICdQYXR0ZXJuXzQnLCAnUGF0dGVybl81JywgJ1Vua25vd25fMScsICdVbmtub3duXzInKQpwYXR0ZXJuX2NvbG9ycyA8LSBjKCdvcmFuZ2UnLCAnZ3JlZW4yJywgJ3JlZCcsICdyb3lhbGJsdWUyJywgJ3B1cnBsZScsICdncmF5NDAnLCAnYmxhY2snKQpuYW1lcyhwYXR0ZXJuX2NvbG9ycykgPC0gY2xvbmVfcGF0dGVybnMKYGBgCgpgYGB7cn0KcGxvdENsb25lRHluIDwtIGZ1bmN0aW9uKGNsb25lX3BhdHRlcm4sIHBhdHRlcm5fY29sb3IsIHRpdCA9ICIiKSB7CiAgICAjIEdldCBjZWxscyBvZiBpbnRlcmVzdAogICAgc3MgPC0gc3Vic2V0KGNsb25lcywgaWRlbnRzID0gY2xvbmVfcGF0dGVybikKICAgIGhpZ2hsaWdodGVkX2NlbGxzIDwtIHJvd25hbWVzKHNzQG1ldGEuZGF0YSkKICAgIAogICAgIyBQbG90IGFsbCBjZWxscyBpbiBncmV5LCBoaWdobGlnaHQgdGhlIHBhdHRlcm4gaW4geW91ciBjaG9zZW4gY29sb3IKICAgIERpbVBsb3QoY2xvbmVzLCAKICAgICAgICAgICAgcmVkdWN0aW9uID0gImZhIiwgCiAgICAgICAgICAgIGNlbGxzLmhpZ2hsaWdodCA9IGhpZ2hsaWdodGVkX2NlbGxzLAogICAgICAgICAgICBzaXplcy5oaWdobGlnaHQgPSAwLjI1LAogICAgICAgICAgICBjb2xzLmhpZ2hsaWdodCA9IHBhdHRlcm5fY29sb3IsCiAgICAgICAgICAgIGNvbHMgPSAiZ3JleTkwIiwgCiAgICAgICAgICAgIHB0LnNpemUgPSAwLjEpICsgCiAgICBnZ3RpdGxlKHRpdCkgJiBOb0xlZ2VuZCgpICYgTm9BeGVzKCkKfQpgYGAKCgojIyMjIEZpZyAzZQpGb3JjZUF0bGFzIGVtYmVkZGluZyBzaG93aW5nIHJlcHJlc2VudGF0aXZlIGNsb25lcyBvZiBlYWNoIHBhdHRlcm4gd2l0aCBjZWxscyBjb2xvcmVkIGJ5IHBhdHRlcm4KYGBge3IgZmlnLmhlaWdodD00LCBmaWcud2lkdGg9OCwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KY2RwIDwtIChsYXBwbHkoMTo3LCBmdW5jdGlvbihpKSBwbG90Q2xvbmVEeW4oY2xvbmVfcGF0dGVybnNbaV0sIHBhdHRlcm5fY29sb3JzW2ldLCB0aXQ9Y2xvbmVfcGF0dGVybnNbaV0pKSkKcF9jb21iaW5lZCA8LSBnZ2FycmFuZ2UocGxvdGxpc3QgPSBjZHAsIG5jb2wgPSA0LCBucm93ID0gMikKcF9jb21iaW5lZApgYGAKCmBgYHtyfQpnZXRDbG9uZUNlbGxJRHMgPC0gZnVuY3Rpb24oaWQpIHsKICAgIGNlbGxJRHMgPC0gcm93bmFtZXMoc3Vic2V0KGNsb25lcyxpZGVudHM9YyhpZCkpQG1ldGEuZGF0YSkKfQpjbG9uZV9jZWxsX2lkcyA8LSBsYXBwbHkoc2V0TmFtZXMoY2xvbmVfcGF0dGVybnMsIGNsb25lX3BhdHRlcm5zKSwgZ2V0Q2xvbmVDZWxsSURzKQpgYGAKCgpgYGB7cn0KcGxvdFBoZW5vV2l0aEJhY2tncm91bmQgPC0gZnVuY3Rpb24oY2xvbmVfcGF0dGVybiwgcGhlbm9fY29sLCB0aXRsZSA9ICIiLCBzZXVyYXRfb2JqID0gY2xvbmVzKSB7CiAgICAjIENoZWNrIGlmICdmYScgcmVkdWN0aW9uIGV4aXN0cwogICAgaWYgKCEiZmEiICVpbiUgbmFtZXMoc2V1cmF0X29iakByZWR1Y3Rpb25zKSkgewogICAgICAgIHN0b3AoIlRoZSAnZmEnIHJlZHVjdGlvbiBpcyBub3QgZm91bmQgaW4gdGhlIFNldXJhdCBvYmplY3QuIikKICAgIH0KCiAgICAjIEV4dHJhY3QgRkEgY29vcmRpbmF0ZXMgYW5kIG1ldGFkYXRhCiAgICBjb29yZHMgPC0gYXMuZGF0YS5mcmFtZShFbWJlZGRpbmdzKHNldXJhdF9vYmosIHJlZHVjdGlvbiA9ICJmYSIpKQogICAgY29vcmRzJGNlbGwgPC0gcm93bmFtZXMoY29vcmRzKQoKICAgICMgR2V0IG1ldGFkYXRhCiAgICBtZXRhIDwtIHNldXJhdF9vYmpAbWV0YS5kYXRhWywgYygiQ2xvbmVfRHluYW1pY3MiLCAiUGhlbm8iKV0KICAgIG1ldGEkY2VsbCA8LSByb3duYW1lcyhtZXRhKQoKICAgICMgTWVyZ2UgY29vcmRpbmF0ZXMgYW5kIG1ldGFkYXRhCiAgICBkYXQgPC0gbWVyZ2UoY29vcmRzLCBtZXRhLCBieSA9ICJjZWxsIikKCiAgICAjIEZsYWcgaGlnaGxpZ2h0ZWQgY2VsbHMKICAgIGRhdCRoaWdobGlnaHQgPC0gZGF0JENsb25lX0R5bmFtaWNzID09IGNsb25lX3BhdHRlcm4KICAgIGRhdCRQaGVubyA8LSBkcm9wbGV2ZWxzKGRhdCRQaGVubykKCiAgICAjIFNwbGl0IGJhY2tncm91bmQgYW5kIGZvcmVncm91bmQKICAgIGJnX2RhdCA8LSBkYXRbIWRhdCRoaWdobGlnaHQsIF0KICAgIGZnX2RhdCA8LSBkYXRbZGF0JGhpZ2hsaWdodCwgXQoKICAgIGlmIChucm93KGZnX2RhdCkgPT0gMCkgewogICAgICAgIHdhcm5pbmcocGFzdGUoIk5vIGNlbGxzIGZvdW5kIGZvciBDbG9uZV9EeW5hbWljcyBwYXR0ZXJuOiIsIGNsb25lX3BhdHRlcm4pKQogICAgICAgIHJldHVybihnZ3Bsb3QoKSArIGdndGl0bGUocGFzdGUoY2xvbmVfcGF0dGVybiwgIihubyBjZWxscykiKSkpCiAgICB9CgogICAgIyBQbG90CiAgICBwIDwtIGdncGxvdCgpICsKICAgICAgICBnZW9tX3BvaW50KGRhdGEgPSBiZ19kYXQsIGFlcyh4ID0gRkFfMSwgeSA9IEZBXzIpLCBjb2xvciA9ICJncmF5OTAiLCBzaXplID0gMC4yKSArCiAgICAgICAgZ2VvbV9wb2ludChkYXRhID0gZmdfZGF0LCBhZXMoeCA9IEZBXzEsIHkgPSBGQV8yLCBjb2xvciA9IFBoZW5vKSwgc2l6ZSA9IDEpICsKICAgICAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gcGhlbm9fY29sLCBkcm9wID0gRkFMU0UpICsKICAgICAgICBnZ3RpdGxlKHRpdGxlKSArCiAgICAgICAgdGhlbWVfdm9pZCgpICsKICAgICAgICB0aGVtZSgKICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAidHJhbnNwYXJlbnQiLCBjb2xvciA9IE5BKSwKICAgICAgICAgICAgcGxvdC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAidHJhbnNwYXJlbnQiLCBjb2xvciA9IE5BKQogICAgICAgICkKCiAgICByZXR1cm4ocCkKfQpgYGAKCkZvcmNlQXRsYXMgZW1iZWRkaW5nIHNob3dpbmcgY2VsbHMgY29sb3JlZCBieSBTQ0xDIHBoZW5vdHlwZQpgYGB7ciBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD00LjV9CkRpbVBsb3QoY2xvbmVzLCBncm91cC5ieT0nUGhlbm8nLCBjb2xzPXBoZW5vX2NvbCwgcmVkdWN0aW9uPSdmYScsIGxhYmVsPUZBTFNFLCBsYWJlbC5zaXplPTYsIHB0LnNpemUgPSAwLjA1KSAmIAogICAgTm9BeGVzKCkKYGBgCgoKIyMjIyBGaWcgM2YKRm9yY2VBdGxhcyBlbWJlZGRpbmcgc2hvd2luZyByZXByZXNlbnRhdGl2ZSBjbG9uZXMgb2YgZWFjaCBwYXR0ZXJuIHdpdGggY2VsbHMgY29sb3JlZCBieSBTQ0xDIHBoZW5vdHlwZQpgYGB7ciBmaWcuaGVpZ2h0PTQsIGZpZy53aWR0aD04LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpwbG90cyA8LSBsYXBwbHkoY2xvbmVfcGF0dGVybnMsIGZ1bmN0aW9uKHBhdCkgcGxvdFBoZW5vV2l0aEJhY2tncm91bmQocGF0LCBwaGVub19jb2wsIHRpdGxlID0gcGF0KSkKcF9jb21iaW5lZCA8LSBnZ2FycmFuZ2UocGxvdGxpc3QgPSBwbG90cywgbmNvbCA9IDQsIG5yb3cgPSAyKQpwX2NvbWJpbmVkCmBgYAoKCiMjIyBWaXN1YWxpemUgaW5kaXZpZHVhbCBjbG9uZXMKRXh0IERhdGEgRmlnLiA2YixjCmBgYHtyfQpwbG90UGF0dGVybkR5bkJ5Q2xvbmUgPC0gZnVuY3Rpb24oY2xvbmVfcGF0dGVybikgewogICAgSWRlbnRzKGNsb25lcyk8LSdDbG9uZV9EeW5hbWljcycKICAgIHAxIDwtIHN1YnNldChjbG9uZXMsaWRlbnRzPWMoY2xvbmVfcGF0dGVybikpCiAgICAKICAgIHBhdHRlcm5fY29sb3IgPC0gcGF0dGVybl9jb2xvcnNbY2xvbmVfcGF0dGVybl0KCiAgICB0ZXN0IDwtIGFzLmRhdGEuZnJhbWUocDEkQ2VsbFRhZ19DbG9uZSkKICAgIHRlc3QkQmFyY29kZXMgPC0gcm93bmFtZXModGVzdCkKICAgICMgR3JvdXAgYnkgQ2VsbFRhZ19DbG9uZQogICAgdGVzdCA8LSB0ZXN0ICU+JSBncm91cF9ieShwMSRDZWxsVGFnX0Nsb25lKQogICAgdGVzdCA8LSBkcGx5cjo6Z3JvdXBfc3BsaXQodGVzdCkKICAgIAogICAgbl9jbG9uZXMgPC0gbGVuZ3RoKHRlc3QpCiAgICAjIFBsb3QgaW4gZm9yIGxvb3AgYWxsIFJQTSBjbG9uZXMgaW4gUGF0dGVybiAxCiAgICBwbG90X2xzdCA8LSB2ZWN0b3IoImxpc3QiLCBsZW5ndGggPSBuX2Nsb25lcykKICAgIGZvciAoaSBpbiBzZXEobl9jbG9uZXMpKSB7CiAgICAgIGcgPC0gRGltUGxvdChUQk9fc2V1cmF0LCBncm91cC5ieT0iQ2VsbFRhZ19DbG9uZSIsIHJlZHVjdGlvbj0nZmEnLCBvcmRlcj1UUlVFLCAKICAgICAgICAgICAgICAgICAgIGNlbGxzLmhpZ2hsaWdodD10ZXN0W1tpXV0kQmFyY29kZXMsc2l6ZXMuaGlnaGxpZ2h0PTIsIAogICAgICAgICAgICAgICAgICAgY29scy5oaWdobGlnaHQ9cGF0dGVybl9jb2xvcikgKwogICAgICAgICAgZ2d0aXRsZShwYXN0ZTAodGVzdFtbaV1dJGBwMSRDZWxsVGFnX0Nsb25lYFsxXSkpICsgCiAgICAgICAgICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA4LGZhY2UgPSAicGxhaW4iKSkgJiBOb0xlZ2VuZCgpICYgCiAgICAgICAgICBOb0F4ZXMoKQogICAgICBwbG90X2xzdFtbaV1dIDwtIGcKICAgIH0KICAgIAogICAgIyBDb21iaW5lIG11bHRpcGxlIHBsb3RzIGZvciBvdXRwdXQsIGFzIGRlc2lyZWQKICAgIHJldHVybihjb3dwbG90OjpwbG90X2dyaWQocGxvdGxpc3QgPSBwbG90X2xzdCwgbmNvbD01KSkKfQpgYGAKCmBgYHtyfQpwYXR0ZXJuX3Bsb3RfbGlzdCA8LSBsYXBwbHkoY2xvbmVfcGF0dGVybnMsIHBsb3RQYXR0ZXJuRHluQnlDbG9uZSkKCm5fcGxvdHNfcGVyX3BhdHRlcm4gPC0gc2FwcGx5KHBhdHRlcm5fcGxvdF9saXN0LCBmdW5jdGlvbih4KSBsZW5ndGgoeFtbJ2xheWVycyddXSkpCgojIyBjYWxjdWxhdGUgaGVpZ2h0IG9mIGZpZ3VyZSBiYXNlZCBvbiBudW1iZXIgb2YgcGxvdHMgcGVyIHBhdHRlcm4KcGx0X2hlaWdodCA8LSAyLjY2NyAqICgobl9wbG90c19wZXJfcGF0dGVybiAlLyUgNSkgKyAxKQpgYGAKCiMjIyMgRXh0IERhdGEgRmlnIDZiLGMKYGBge3IgZmlnLmhlaWdodD0xNiwgZmlnLndpZHRoPTEyfQpwYXR0ZXJuX3Bsb3RfbGlzdFtbMV1dCmBgYAoKCmBgYHtyIGZpZy5oZWlnaHQ9NS4zMywgZmlnLndpZHRoPTEyfQpwYXR0ZXJuX3Bsb3RfbGlzdFtbMl1dCmBgYApgYGB7ciBmaWcuaGVpZ2h0PTEwLjY2NywgZmlnLndpZHRoPTEyfQpwYXR0ZXJuX3Bsb3RfbGlzdFtbM11dCmBgYApgYGB7ciBmaWcuaGVpZ2h0PTEzLjMzMywgZmlnLndpZHRoPTEyfQpwYXR0ZXJuX3Bsb3RfbGlzdFtbNF1dCmBgYAoKYGBge3IgZmlnLmhlaWdodD0yLjY2NywgZmlnLndpZHRoPTEyfQpwYXR0ZXJuX3Bsb3RfbGlzdFtbNV1dCmBgYAoKYGBge3IgZmlnLmhlaWdodD01LjMzLCBmaWcud2lkdGg9MTJ9CnBhdHRlcm5fcGxvdF9saXN0W1s2XV0KYGBgCgpgYGB7ciBmaWcuaGVpZ2h0PTIuNjY3LCBmaWcud2lkdGg9MTJ9CnBhdHRlcm5fcGxvdF9saXN0W1s3XV0KYGBgCiMjIFZpc3VhbGl6ZSBvbmx5IGNsb25lcyBtYXRjaGluZyBpbiB2aXZvIGluIEZBIHByb2plY3Rpb24KRXh0IERhdGEgRmlnLiA3ZgoKYGBge3IgZmlnLmhlaWdodD0zLCBmaWcud2lkdGg9M30KSWRlbnRzKGNsb25lcykgPC0gIkNlbGxUYWdfQ2xvbmUiCiMgU3Vic2V0IGp1c3QgdGhlIGNsb25lcyB0aGF0IG1hdGNoIHRoZSBpbiB2aXRybywgcGF0dGVybiAxCmludml0cm9tYXRjaCA8LSBzdWJzZXQoY2xvbmVzLCBpZGVudHM9YygiUlBNX0Nsb25lXzE0IiwiUlBNX0Nsb25lXzIiLCJSUE1fQ2xvbmVfMzMiLCJSUE1fQ2xvbmVfMzYiLCJSUE1fQ2xvbmVfNiIpKQoKcDFfY2VsbHMgPC0gY29sbmFtZXMoaW52aXRyb21hdGNoKQpwMl9jZWxscyA8LSBjb2xuYW1lcyhzdWJzZXQoY2xvbmVzLCBpZGVudHM9IlJQTV9DbG9uZV8yMyIpKQpwNV9jZWxscyA8LSBjb2xuYW1lcyhzdWJzZXQoY2xvbmVzLCBpZGVudHM9IlJQTV9DbG9uZV8xMyIpKQoKRGltUGxvdChUQk9fc2V1cmF0LCBncm91cC5ieT0iQ2VsbFRhZ19DbG9uZSIsIHJlZHVjdGlvbj0nZmEnLCAKICAgICAgICBvcmRlcj1UUlVFLCBjZWxscy5oaWdobGlnaHQ9cDFfY2VsbHMsIHNpemVzLmhpZ2hsaWdodD0yLCAKICAgICAgICBjb2xzLmhpZ2hsaWdodD1jKCJvcmFuZ2UiKSkgKyBnZ3RpdGxlKCIiKSAmIE5vTGVnZW5kKCkgJiBOb0F4ZXMoKQpEaW1QbG90KFRCT19zZXVyYXQsIGdyb3VwLmJ5PSJDZWxsVGFnX0Nsb25lIiwgcmVkdWN0aW9uPSdmYScsIAogICAgICAgIG9yZGVyPVRSVUUsIGNlbGxzLmhpZ2hsaWdodD1wMl9jZWxscywgc2l6ZXMuaGlnaGxpZ2h0PTIsIAogICAgICAgIGNvbHMuaGlnaGxpZ2h0PXBhdHRlcm5fY29sb3JzWyJQYXR0ZXJuXzIiXSkgKyBnZ3RpdGxlKCIiKSAmIE5vTGVnZW5kKCkgJiBOb0F4ZXMoKQpEaW1QbG90KFRCT19zZXVyYXQsIGdyb3VwLmJ5PSJDZWxsVGFnX0Nsb25lIiwgcmVkdWN0aW9uPSdmYScsIAogICAgICAgIG9yZGVyPVRSVUUsIGNlbGxzLmhpZ2hsaWdodD1wNV9jZWxscywgc2l6ZXMuaGlnaGxpZ2h0PTIsIAogICAgICAgIGNvbHMuaGlnaGxpZ2h0PXBhdHRlcm5fY29sb3JzWyJQYXR0ZXJuXzUiXSkgKyBnZ3RpdGxlKCIiKSAmIE5vTGVnZW5kKCkgJiBOb0F4ZXMoKQoKCmBgYAoKCgojIyMjIEZpZy4gM2cgCmRwdCBwc3VlZG90aW1lIGluIEZBIHNwYWNlIApgYGB7ciBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD0zLjc1LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpGZWF0dXJlUGxvdChUQk9fc2V1cmF0LCBmZWF0dXJlcyA9IGMoImRwdF9wc2V1ZG90aW1lIiksIHB0LnNpemU9MC4wMSwKICAgICAgICAgICAgcmVkdWN0aW9uPSdmYScsKSArIHNjYWxlX2NvbG9yX3ZpcmlkaXMob3B0aW9uPSJ2aXJpZGlzIixkaXJlY3Rpb249LTEpJiBOb0F4ZXMoKQoKYGBgCiMjIyMgRnVuY3Rpb24gdG8gcGxvdCBjbG9uZSBkeW5hbWljcyBjb2xvcmVkIGJ5IERQVApgYGB7cn0KcGxvdENsb25lRHluRHB0IDwtIGZ1bmN0aW9uKGNsb25lX3BhdHRlcm4sIHNldXJhdF9vYmosIHRpdGxlID0gIiIpIHsKICAgICMgU3Vic2V0IGNlbGxzIG9mIGludGVyZXN0CiAgICBzcyA8LSBzdWJzZXQoc2V1cmF0X29iaiwgaWRlbnRzID0gY2xvbmVfcGF0dGVybikKICAgIGhpZ2hsaWdodF9jZWxscyA8LSBjb2xuYW1lcyhzcykKCiAgICAjIEV4dHJhY3QgZW1iZWRkaW5ncyBhbmQgbWV0YWRhdGEKICAgIGZhX2Nvb3JkcyA8LSBFbWJlZGRpbmdzKHNldXJhdF9vYmosICJmYSIpCiAgICBkcHRfdmFscyA8LSBzZXVyYXRfb2JqQG1ldGEuZGF0YSRkcHRfcHNldWRvdGltZQogICAgbmFtZXMoZHB0X3ZhbHMpIDwtIHJvd25hbWVzKHNldXJhdF9vYmpAbWV0YS5kYXRhKQoKICAgIGRhdCA8LSBhcy5kYXRhLmZyYW1lKGZhX2Nvb3JkcykKICAgIGRhdCRoaWdobGlnaHQgPC0gaWZlbHNlKHJvd25hbWVzKGRhdCkgJWluJSBoaWdobGlnaHRfY2VsbHMsICJ5ZXMiLCAibm8iKQogICAgZGF0JHBzZXVkb3RpbWUgPC0gZHB0X3ZhbHNbcm93bmFtZXMoZGF0KV0KICAgIGNvbG5hbWVzKGRhdClbMToyXSA8LSBjKCJGQTEiLCAiRkEyIikgICMgbmFtZSBjb2x1bW5zIGZvciBjbGFyaXR5CgogICAgbGlicmFyeShnZ3Bsb3QyKQogICAgZ2dwbG90KGRhdCwgYWVzKHggPSBGQTEsIHkgPSBGQTIpKSArCiAgICAgICAgIyBCYWNrZ3JvdW5kIGNlbGxzCiAgICAgICAgZ2VvbV9wb2ludChkYXRhID0gc3Vic2V0KGRhdCwgaGlnaGxpZ2h0ID09ICJubyIpLCAKICAgICAgICAgICAgICAgICAgIGNvbG9yID0gImdyZXk4NSIsIHNpemUgPSAwLjEpICsKICAgICAgICAjIEhpZ2hsaWdodGVkIGNlbGxzIGNvbG9yZWQgYnkgcHNldWRvdGltZQogICAgICAgIGdlb21fcG9pbnQoZGF0YSA9IHN1YnNldChkYXQsIGhpZ2hsaWdodCA9PSAieWVzIiksIAogICAgICAgICAgICAgICAgICAgYWVzKGNvbG9yID0gcHNldWRvdGltZSksIHNpemUgPSAwLjI1KSArCiAgICAgICAgc2NhbGVfY29sb3JfdmlyaWRpc19jKG9wdGlvbiA9ICJ0dXJibyIsIGRpcmVjdGlvbiA9IC0xKSArCiAgICAgICAgZ2d0aXRsZSh0aXRsZSkgKwogICAgICAgIHRoZW1lX3ZvaWQoKSArCiAgICAgICAgdGhlbWUoCiAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpLCAgICAgICMgcmVtb3ZlcyBhbnkgcGFuZWwgYm9yZGVyCiAgICAgICAgICAgIHBsb3QuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gInRyYW5zcGFyZW50IiwgY29sb3IgPSBOQSksCiAgICAgICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJ0cmFuc3BhcmVudCIsIGNvbG9yID0gTkEpLAogICAgICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpCiAgICAgICAgKQp9CmBgYAoKCgojIyMjIEZpZyAzaApgYGB7ciBmaWcuaGVpZ2h0PTIsIGZpZy53aWR0aD0xMiwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KSWRlbnRzKGNsb25lcykgPC0gJ0Nsb25lX0R5bmFtaWNzJwoKbXlfcGF0cyA8LSBjKCdQYXR0ZXJuXzEnLCdQYXR0ZXJuXzInLCdQYXR0ZXJuXzUnLCdVbmtub3duXzEnLCdQYXR0ZXJuXzMnLCdQYXR0ZXJuXzQnKQoKcGxvdHMgPC0gbGFwcGx5KG15X3BhdHMsIGZ1bmN0aW9uKHBhdCkgewogICAgdGl0IDwtIGdzdWIocGF0LCAnXycsICcgJykKICAgIHBsb3RDbG9uZUR5bkRwdChwYXQsIGNsb25lcywgdGl0bGUgPSB0aXQpCn0pCgpjb3dwbG90OjpwbG90X2dyaWQocGxvdGxpc3QgPSBwbG90cywgbmNvbD02KQpgYGAKCg==